import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'dart:io';

import 'package:collection/collection.dart';
import 'package:common_utils/common_utils.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_openim_sdk/flutter_openim_sdk.dart';
import 'package:get/get.dart';
import 'package:openim_common/openim_common.dart';
import 'package:pull_to_refresh_new/pull_to_refresh.dart';
import 'package:rxdart/rxdart.dart';
import 'package:sprintf/sprintf.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:video_compress/video_compress.dart';
import 'package:wechat_assets_picker/wechat_assets_picker.dart';
import 'package:wechat_camera_picker/wechat_camera_picker.dart';
import 'package:openim_live/openim_live.dart';

import '../../core/controller/app_controller.dart';
import '../../core/controller/im_controller.dart';
import '../../core/im_callback.dart';
import '../../routes/app_navigator.dart';
import '../contacts/select_contacts/select_contacts_logic.dart';
import '../contacts/user_profile_panel/user_profile _panel_logic.dart';
import '../conversation/conversation_logic.dart';
import 'group_setup/group_member_list/group_member_list_logic.dart';

class ChatLogic extends SuperController {
  final imLogic = Get.find<IMController>();
  final appLogic = Get.find<AppController>();
  final conversationLogic = Get.find<ConversationLogic>();
  final cacheLogic = Get.find<CacheController>();
    // 添加发送进度流
  final sendProgressSub = PublishSubject<MsgStreamEv<int>>();
    // 添加消息发送进度回调函数
  Function(String msgId, int progress)? onMsgSendProgress;


  // 语音录制相关变量
  VoiceRecord? _voiceRecord;
  final isRecording = false.obs;
  final recordDuration = 0.obs;
  Timer? _recordTimer;

  final inputCtrl = TextEditingController();
  final focusNode = FocusNode();
  final scrollController = ScrollController();
  final refreshController = RefreshController();
  bool playOnce = false;

  final forceCloseToolbox = PublishSubject<bool>();
  final sendStatusSub = PublishSubject<MsgStreamEv<bool>>();

  late ConversationInfo conversationInfo;
  Message? searchMessage;
  final nickname = ''.obs;
  final faceUrl = ''.obs;
  Timer? _debounce;
  var messageList = <Message>[].obs;
  final tempMessages = <Message>[];
  final scaleFactor = Config.textScaleFactor.obs;
  final background = "".obs;
  final memberUpdateInfoMap = <String, GroupMembersInfo>{};
  final memberDomainAvatar = <String, String>{};
  final memberDomainNickname = <String, String>{};
  final isGroupDomainAvatarsReady = false.obs;
  final groupMessageReadMembers = <String, List<String>>{};
  final groupMemberRoleLevel = 1.obs;
  GroupInfo? groupInfo;
  GroupMembersInfo? groupMembersInfo;
  List<GroupMembersInfo> ownerAndAdmin = [];

  final isInGroup = true.obs;
  final memberCount = 0.obs;
  final privateMessageList = <Message>[];
  final isInBlacklist = false.obs;

  final scrollingCacheMessageList = <Message>[];
  final announcement = ''.obs;
  late StreamSubscription conversationSub;
  late StreamSubscription memberAddSub;
  late StreamSubscription memberDelSub;
  late StreamSubscription joinedGroupAddedSub;
  late StreamSubscription joinedGroupDeletedSub;
  late StreamSubscription memberInfoChangedSub;
  late StreamSubscription groupInfoUpdatedSub;
  late StreamSubscription friendInfoChangedSub;
  StreamSubscription? userStatusChangedSub;
  StreamSubscription? selfInfoUpdatedSub;

  late StreamSubscription connectionSub;
  final syncStatus = IMSdkStatus.syncEnded.obs;
  int? lastMinSeq;

  final showCallingMember = false.obs;

  bool _isReceivedMessageWhenSyncing = false;
  bool _isStartSyncing = false;
  bool _isFirstLoad = true;

  final copyTextMap = <String?, String?>{};

  String? groupOwnerID;

  final _pageSize = 40;

  // 置顶消息相关变量
  final pinnedMessages = <Message>[].obs;
  final showPinnedMessages = false.obs;

  // 引用消息相关变量
  final quotedMessage = Rxn<Message>();

  // 多选功能相关变量
  final isMultiSelectMode = false.obs;
  final selectedMessages = <Message>[].obs;
  // 群人数显示控制变量
  final showGroupMemberCount = true.obs;

  RTCBridge? get rtcBridge => PackageBridge.rtcBridge;

  bool get rtcIsBusy => rtcBridge?.hasConnection == true;

  String? get userID => conversationInfo.userID;

  String? get groupID => conversationInfo.groupID;

  bool get isSingleChat => null != userID && userID!.trim().isNotEmpty;

  bool get isGroupChat => null != groupID && groupID!.trim().isNotEmpty;

  String get memberStr => isSingleChat ? "" : "($memberCount)";

  String? get senderName {
    if (isSingleChat) return OpenIM.iMManager.userInfo.nickname;
    final uid = OpenIM.iMManager.userID;
    final dn = memberDomainNickname[uid];
    if (dn != null && dn.isNotEmpty) return dn;
    return groupMembersInfo?.nickname ?? OpenIM.iMManager.userInfo.nickname;
  }

  bool get isAdminOrOwner =>
      groupMemberRoleLevel.value == GroupRoleLevel.admin || groupMemberRoleLevel.value == GroupRoleLevel.owner;

  final directionalUsers = <GroupMembersInfo>[].obs;

  
  void onQuoteMessage(Message message) {
    // 设置要引用的消息
    quotedMessage.value = message;
    
    // 聚焦到输入框
    focusNode.requestFocus();
    
    // 关闭工具箱
    closeToolbox();
    
    // IMViews.showToast('已选择引用消息');
  }

  // 清除引用消息
  void clearQuoteMessage() {
    quotedMessage.value = null;
  }
    // 获取引用消息的显示内容
  String? get quoteContent {
    final message = quotedMessage.value;
    if (message == null) return null;
    
    // 根据消息类型返回不同的显示内容
    switch (message.contentType) {
      case MessageType.text:
        return message.textElem?.content ?? '';
      case MessageType.atText:
        // @消息，显示文本内容
        return message.atTextElem?.text ?? '';
      case MessageType.picture:
        return '[图片]';
      case MessageType.video:
        return '[视频]';
      case MessageType.voice:
        return '[语音]';
      case MessageType.file:
        return '[文件] ${message.fileElem?.fileName ?? ''}';
      case MessageType.location:
        return '[位置] ${message.locationElem?.description ?? ''}';
      case MessageType.card:
        return '[名片] ${message.cardElem?.nickname ?? ''}';
      case MessageType.quote:
        return message.quoteElem?.text ?? '';
      case MessageType.customFace:
        return '[表情]';
      case MessageType.typing:
        return '[正在输入...]';
      case MessageType.custom:
        // 自定义消息，根据 customType 返回不同内容
        final data = IMUtils.parseCustomMessage(message);
        if (data != null) {
          final customType = data['customType'];
          switch (customType) {
            case CustomMessageType.emoji:
              return '[表情]';
            case CustomMessageType.call:
              final callData = data['data'];
              if (callData != null) {
                final type = callData['type'];
                return type == 'video' ? '[视频通话]' : '[语音通话]';
              }
              return '[通话]';
            case CustomMessageType.tag:
              return '[标签]';
            case CustomMessageType.meeting:
              return '[会议]';
            case CustomMessageType.moments:
              return '[朋友圈]';
            case CustomMessageType.blockedByFriend:
              return '[已被好友拉黑]';
            case CustomMessageType.deletedByFriend:
              return '[已被好友删除]';
            case CustomMessageType.removedFromGroup:
              return '[已被移出群聊]';
            case CustomMessageType.groupDisbanded:
              return '[群聊已解散]';
            default:
              return '[自定义消息]';
          }
        }
        return '[自定义消息]';
      // 系统通知消息
      case MessageType.groupCreatedNotification:
        return '[群聊创建通知]';
      case MessageType.groupInfoSetNotification:
        return '[群信息变更通知]';
      case MessageType.memberQuitNotification:
        return '[成员退出通知]';
      case MessageType.memberInvitedNotification:
        return '[成员邀请通知]';
      case MessageType.memberKickedNotification:
        return '[成员被踢通知]';
      case MessageType.memberEnterNotification:
        return '[成员进入通知]';
      case MessageType.dismissGroupNotification:
        return '[群聊解散通知]';
      case MessageType.groupOwnerTransferredNotification:
        return '[群主转让通知]';
      case MessageType.groupMemberMutedNotification:
        return '[群成员禁言通知]';
      case MessageType.groupMemberCancelMutedNotification:
        return '[群成员解除禁言通知]';
      case MessageType.groupMutedNotification:
        return '[群聊禁言通知]';
      case MessageType.groupCancelMutedNotification:
        return '[群聊解除禁言通知]';
      case MessageType.friendApplicationApprovedNotification:
        return '[好友申请通过通知]';
      case MessageType.burnAfterReadingNotification:
        return '[阅后即焚通知]';
      case MessageType.groupMemberInfoChangedNotification:
        return '[群成员信息变更通知]';
      case MessageType.groupInfoSetAnnouncementNotification:
        return '[群公告设置通知]';
      case MessageType.groupInfoSetNameNotification:
        return '[群名称变更通知]';
      case MessageType.revokeMessageNotification:
        return '[消息撤回通知]';
      default:
        return '[未知消息类型]';
    }
  }
  void _initVoiceRecord() {
    _voiceRecord = VoiceRecord(
      maxRecordSec: 60, // 最大录制60秒
      onFinished: (duration, path) {
        _onRecordFinished(duration, path);
      },
      onInterrupt: (duration, path) {
        _onRecordInterrupt(duration, path);
      },
      onDuration: (duration) {
        recordDuration.value = duration;
      },
    );
  }
    // 检查麦克风权限
  Future<bool> _checkMicrophonePermission() async {
    final completer = Completer<bool>();
    
    Permissions.microphone(() {
      completer.complete(true);
    });
    
    // 设置超时，避免无限等待
    Timer(Duration(seconds: 5), () {
      if (!completer.isCompleted) {
        completer.complete(false);
      }
    });
    
    return completer.future;
  }
  // 开始语音录制
  Future<void> onStartVoiceRecord() async {
    try {
      // 检查录音权限
      // 检查录音权限
      final hasPermission = await _checkMicrophonePermission();
      if (!hasPermission) {
        IMViews.showToast('需要麦克风权限才能录音');
        return;
      }

      // 初始化语音录制器（如果还未初始化）
      if (_voiceRecord == null) {
        _initVoiceRecord();
      }

      // 检查当前是否已在录音
      if (isRecording.value) {
        Logger.print('已在录音中，忽略重复开始录音请求');
        return;
      }

      // 重置录音时长
      recordDuration.value = 0;
      
      // 开始录音
      await _voiceRecord?.start();
      
      // 更新录音状态
      isRecording.value = true;
      
      Logger.print('开始语音录制');
      
    } catch (e) {
      Logger.print('开始录音失败: $e');
      IMViews.showToast('录音启动失败');
      
      // 重置状态
      isRecording.value = false;
      recordDuration.value = 0;
    }
  }
  // 停止语音录制
  Future<void> onStopVoiceRecord() async {
    try {
      if (!isRecording.value || _voiceRecord == null) {
        return;
      }

      // 停止录音
      await _voiceRecord?.stop();
      
      // 更新录音状态
      isRecording.value = false;
      
      Logger.print('停止语音录制');
      
    } catch (e) {
      Logger.print('停止录音失败: $e');
      isRecording.value = false;
      recordDuration.value = 0;
    }
  }

  // 取消语音录制
  Future<void> onCancelVoiceRecord() async {
    try {
      if (!isRecording.value || _voiceRecord == null) {
        return;
      }

      // 停止录音（中断模式）
      await _voiceRecord?.stop(isInterrupt: true);
      
      // 重置状态
      isRecording.value = false;
      recordDuration.value = 0;
      
      Logger.print('取消语音录制');
      
    } catch (e) {
      Logger.print('取消录音失败: $e');
      isRecording.value = false;
      recordDuration.value = 0;
    }
  }

  // 录音完成回调
  void _onRecordFinished(int duration, String path) {
    isRecording.value = false;
    
    // 检查录音时长是否太短
    if (duration < 1) {
      IMViews.showToast('录音时间太短');
      _deleteVoiceFile(path);
      return;
    }

    // 发送语音消息
    sendVoice(path: path, duration: duration);
  }

  // 录音中断回调
  void _onRecordInterrupt(int duration, String path) {
    isRecording.value = false;
    recordDuration.value = 0;
    
    if (duration >= 60) {
      // 达到最大录制时长，自动发送
      sendVoice(path: path, duration: duration);
    } else {
      // 其他中断情况，删除文件
      _deleteVoiceFile(path);
    }
  }

  // 发送语音消息
  Future<void> sendVoice({required String path, required int duration}) async {
     try {
      Logger.print('准备发送语音消息: $path, 时长: ${duration}秒');
      
      // 检查文件是否存在
      final file = File(path);
      if (!file.existsSync()) {
        Logger.print('语音文件不存在: $path');
        IMViews.showToast('语音文件不存在');
        return;
      }

      // 检查录音时长是否太短
      if (duration < 1) {
        IMViews.showToast('录音时间太短');
        _deleteVoiceFile(path);
        return;
      }

      // 创建语音消息
      Message? message;
      try {
        message = await OpenIM.iMManager.messageManager.createSoundMessageFromFullPath(
          soundPath: path,
          duration: duration,
        );
        
        if (message == null) {
          throw Exception('createSoundMessage 返回 null');
        }
        
        Logger.print('语音消息创建成功: ${message.clientMsgID}');
      } catch (e) {
        Logger.print('创建语音消息失败: $e');
        IMViews.showToast('语音消息创建失败');
        _deleteVoiceFile(path);
        return;
      }

      // 发送消息
      await _sendMessage(message);
      
      Logger.print('语音消息发送成功: $path, 时长: ${duration}秒');
      
    } catch (e) {
      Logger.print('发送语音消息失败: $e');
      IMViews.showToast('语音发送失败');
      _deleteVoiceFile(path);
    }
  }

  // 删除语音文件
  void _deleteVoiceFile(String path) {
    try {
      final file = File(path);
      if (file.existsSync()) {
        file.deleteSync();
        Logger.print('删除语音文件: $path');
      }
    } catch (e) {
      Logger.print('删除语音文件失败: $e');
    }
  }
  void onTapCamera() async {
    final AssetEntity? asset = await CameraPicker.pickFromCamera(
      Get.context!,
      pickerConfig: const CameraPickerConfig(
        enableRecording: true,
        enableTapRecording: false,
        enableAudio: true,
        enableSetExposure: false,
        enableExposureControlOnPoint: false,
        enablePinchToZoom: true,
        enablePullToZoomInRecord: true,
        shouldDeletePreviewFile: true,
      ),
    );
    if (asset != null) {
      await _handleAssets(asset);
    }
  }

void onTapFile() async {
  FilePickerResult? result = await FilePicker.platform.pickFiles(
    type: FileType.any,
    allowMultiple: false,
  );

  if (result != null && result.files.single.path != null) {
    String filePath = result.files.single.path!;
    
    // 添加路径验证和清理
    Logger.print('原始文件路径: $filePath');
    
    // 确保路径格式正确
    filePath = await IMUtils.toFilePath(filePath);
    Logger.print('处理后文件路径: $filePath');
    
    final file = File(filePath);
    if (!file.existsSync()) {
      Logger.print('文件不存在: $filePath');
      IMViews.showToast('选择的文件不存在');
      return;
    }
    
    await sendFile(path: filePath);
  }
}
  
  // 点击语音按钮
  void onTapVoice() {
    // 关闭工具箱
    forceCloseToolbox.add(true);
    // 取消焦点
    focusNode.unfocus();
  }

Future<void> sendFile({required String path}) async {
  Logger.print('准备发送文件: $path');
  
  final file = File(path);
  if (!file.existsSync()) {
    Logger.print('文件不存在: $path');
    IMViews.showToast(StrRes.fileNotExist);
    return;
  }

  final fileName = file.path.split('/').last;
  Logger.print('文件名: $fileName');
  
  try {
    final message = await OpenIM.iMManager.messageManager.createFileMessageFromFullPath(
      filePath: path,
      fileName: fileName,
    );
    
    Logger.print('文件消息创建成功');
    await _sendMessage(message);
  } catch (e) {
    Logger.print('创建文件消息失败: $e');
    IMViews.showToast('文件发送失败: $e');
  }
}

   Future<void> sendVideo({required String path, bool sendNow = true}) async {
    // 添加路径验证
    if (path.isEmpty) {
      debugPrint('视频路径为空');
      IMViews.showToast('视频路径无效');
      return;
    }
    
    // 处理路径格式
    String processedPath = path;
    try {
      processedPath = await IMUtils.toFilePath(path);
      debugPrint('处理后的路径: $processedPath');
    } catch (e) {
      debugPrint('路径处理失败: $e');
    }
    
    final file = File(processedPath);
    debugPrint('视频文件信息: 路径=$processedPath, 大小=${file.existsSync() ? file.lengthSync() : "文件不存在"}, 存在=${file.existsSync()}');
    
    if (!file.existsSync()) {
      debugPrint('文件不存在，尝试使用原始路径: $path');
      final originalFile = File(path);
      if (!originalFile.existsSync()) {
        IMViews.showToast(StrRes.fileNotExist);
        return;
      }
      processedPath = path;
    }
    
    try {
      // 获取视频信息和实际时长
      int actualDuration = 15; // 默认值
      try {
        final mediaInfo = await IMUtils.getMediaInfo(processedPath);
        // 正确的写法
        if (mediaInfo.duration != null) {
          actualDuration = (mediaInfo.duration! / 1000).round();
        }
        debugPrint('视频时长: ${actualDuration}秒');
      } catch (e) {
        debugPrint('获取视频信息失败，使用默认时长: $e');
      }
      
      // 生成视频缩略图
      String snapshotPath = '';
      try {
        final thumbnailFile = await VideoCompress.getFileThumbnail(
          processedPath,
          quality: 50, // 缩略图质量 (1-100)
          position: 1000, // 从视频第1秒处截取缩略图 (毫秒)
        );
        snapshotPath = thumbnailFile.path;
        debugPrint('缩略图生成成功: $snapshotPath');
      } catch (e) {
        debugPrint('生成缩略图失败，将使用空路径: $e');
        snapshotPath = '';
      }
      
      // 创建视频消息
      final message = await OpenIM.iMManager.messageManager.createVideoMessageFromFullPath(
        videoPath: processedPath,
        videoType: 'mp4',
        duration: actualDuration,
        snapshotPath: snapshotPath,
      );
      debugPrint("创建视频消息成功: $message");
      
      // 发送消息 - 修复：取消注释发送逻辑
      if (sendNow) {
        await _sendMessage(message);
        debugPrint('视频消息发送成功');
      } else {
        tempMessages.add(message);
        debugPrint('视频消息已添加到临时消息列表');
      }
    } catch (e) {
      debugPrint('创建或发送视频消息失败: $e');
      IMViews.showToast('视频发送失败: $e');
      return;
    }
  }
   
  
 
  
bool isCurrentChat(Message message) {
  var senderId = message.sendID;
  var receiverId = message.recvID;
  var groupId = message.groupID;

  var isCurSingleChat = message.isSingleChat &&
      isSingleChat &&
      (senderId == userID || (senderId == OpenIM.iMManager.userID && receiverId == userID));
  var isCurGroupChat = message.isGroupChat && isGroupChat && groupID == groupId;
  return isCurSingleChat || isCurGroupChat;
}

  void scrollBottom() {
    WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
      scrollController.jumpTo(0);
    });
  }

  Future<List<Message>> searchMediaMessage() async {
    final messageList = await OpenIM.iMManager.messageManager.searchLocalMessages(
        conversationID: conversationInfo.conversationID,
        messageTypeList: [MessageType.picture, MessageType.video],
        count: 500);
    return messageList.searchResultItems?.first.messageList?.reversed.toList() ?? [];
  }

  @override
  void onReady() {
    _resetGroupAtType();
    _clearUnreadCount();

    scrollController.addListener(() {
      focusNode.unfocus();
    });
    super.onReady();
  }

  Future<void> _refreshDomainProfile() async {
    if (!isSingleChat || userID == null || userID!.isEmpty) return;
    try {
      final profile = await Apis.getUserProfileByOpenIMID(userID!);
      if (profile != null) {
        conversationInfo.faceURL = profile.faceURL ?? conversationInfo.faceURL;
        conversationInfo.showName = profile.nickname ?? conversationInfo.showName;
        faceUrl.value = conversationInfo.faceURL ?? '';
        nickname.value = conversationInfo.showName ?? nickname.value;
        debugPrint('刷新域名配置 - 昵称: ${conversationInfo.showName}, 头像URL: ${conversationInfo.faceURL}');
      }
    } catch (_) {}
  }

  @override
  void onInit() {
    var arguments = Get.arguments;
    conversationInfo = arguments['conversationInfo'];
    searchMessage = arguments['searchMessage'];
    nickname.value = conversationInfo.showName ?? '';
    faceUrl.value = conversationInfo.faceURL ?? '';
    _refreshDomainProfile();
    _initChatConfig();
    _setSdkSyncDataListener();
    
    if (isGroupChat) {
      _preloadGroupMemberFriendRemarks();
      _preloadGroupMemberDomainAvatars();
    }

    // 加载置顶消息
    _loadPinnedMessages();

    conversationSub = imLogic.conversationChangedSubject.listen((value) {
      final obj = value.firstWhereOrNull((e) => e.conversationID == conversationInfo.conversationID);

      if (obj != null) {
        conversationInfo = obj;
      }
    });

      // 在 onInit 方法中添加置顶通知消息处理
    imLogic.onRecvNewMessage = (Message message) async {
        debugPrint("onRecvNewMessage1: $message");
        if (isCurrentChat(message)) {
          // 处理置顶通知消息
          if (message.contentType == MessageType.custom) {
            final customData = message.customElem?.data;
            if (customData != null) {
              try {
                final data = jsonDecode(customData);
                final extension = jsonDecode(message.customElem?.extension ?? '{}');
                final customType = extension['customType'];
                
                if (customType == CustomMessageType.pinMessage || 
                    customType == CustomMessageType.unpinMessage) {
                  // 处理置顶逻辑
                  await _handlePinNotificationMessage(message, data, customType == CustomMessageType.pinMessage);
                  
                  // 继续处理，让消息显示在聊天界面
                  // 不再直接 return，而是继续执行下面的逻辑
                }
              } catch (e) {
                Logger.print('解析置顶通知消息失败: $e');
              }
            }
          }
          
          if (message.contentType == MessageType.typing) {
          } else {
            if (!messageList.contains(message) && !scrollingCacheMessageList.contains(message)) {
              _isReceivedMessageWhenSyncing = true;
              if (scrollController.offset != 0) {
                scrollingCacheMessageList.add(message);
              } else {
                messageList.add(message);
                scrollBottom();
              }
            }
          }
        }
      };


        imLogic.onRecvMessageRevoked = (RevokedInfo info) {
      // 查找被撤回的消息
      final revokedMessage = messageList.firstWhereOrNull((msg) => msg.clientMsgID == info.clientMsgID);
      
      if (revokedMessage != null) {
        final originalIndex = messageList.indexOf(revokedMessage);
        
        // 从消息列表中移除被撤回的消息
        messageList.removeWhere((msg) => msg.clientMsgID == info.clientMsgID);

        // 从置顶消息列表中移除被撤回的消息
        final wasInPinned = pinnedMessages.any((msg) => msg.clientMsgID == info.clientMsgID);
        if (wasInPinned) {
          pinnedMessages.removeWhere((msg) => msg.clientMsgID == info.clientMsgID);
          _savePinnedMessages(); // 保存更新后的置顶消息列表
          showPinnedMessages.value = pinnedMessages.isNotEmpty; // 更新显示状态
          Logger.print('已从置顶消息中移除被撤回的消息: ${info.clientMsgID}');
        }
        
        // 创建撤回通知消息
        final notificationMessage = Message()
          ..clientMsgID = '${info.clientMsgID}_revoke_${DateTime.now().millisecondsSinceEpoch}'
          ..serverMsgID = ''
          ..createTime = DateTime.now().millisecondsSinceEpoch
          ..sendTime = DateTime.now().millisecondsSinceEpoch
          ..sessionType = conversationInfo.conversationType
          ..sendID = info.revokerID ?? ''
          ..recvID = conversationInfo.conversationType == ConversationType.single ? conversationInfo.userID : ''
          ..groupID = conversationInfo.conversationType == ConversationType.group ? conversationInfo.groupID : ''
          ..contentType = MessageType.revokeMessageNotification
          ..status = MessageStatus.succeeded
          ..isRead = true
          ..notificationElem = NotificationElem()
          ..seq = revokedMessage.seq;
        
        // 设置通知详情
        final detail = {
          'revokerID': info.revokerID,
          'revokerNickname': info.revokerNickname,
          'clientMsgID': info.clientMsgID,
          'revokeTime': info.revokeTime,
        };
        
        notificationMessage.notificationElem!.detail = jsonEncode(detail);
        
        // 在原消息位置插入撤回通知
        if (originalIndex >= 0 && originalIndex < messageList.length) {
          messageList.insert(originalIndex, notificationMessage);
        } else {
          messageList.add(notificationMessage);
        }
        
        // 刷新消息列表
        messageList.refresh();
        
        Logger.print('消息已撤回并添加通知: ${info.clientMsgID}');
      }
    };

    imLogic.onRecvC2CReadReceipt = (List<ReadReceiptInfo> list) {
      try {
        for (var readInfo in list) {
          if (readInfo.userID == userID) {
            for (var e in messageList) {
              if (readInfo.msgIDList?.contains(e.clientMsgID) == true) {
                e.isRead = true;
                e.hasReadTime = _timestamp;
              }
            }
          }
        }
        messageList.refresh();
      } catch (e) {}
    };

    joinedGroupAddedSub = imLogic.joinedGroupAddedSubject.listen((event) {
      if (event.groupID == groupID) {
        isInGroup.value = true;
        _queryGroupInfo();
      }
    });

    joinedGroupDeletedSub = imLogic.joinedGroupDeletedSubject.listen((event) {
      if (event.groupID == groupID) {
        isInGroup.value = false;
        inputCtrl.clear();
      }
    });

    memberAddSub = imLogic.memberAddedSubject.listen((info) {
      var groupId = info.groupID;
      if (groupId == groupID) {
        _putMemberInfo([info]);
      }
    });

    memberDelSub = imLogic.memberDeletedSubject.listen((info) {
      if (info.groupID == groupID && info.userID == OpenIM.iMManager.userID) {
        isInGroup.value = false;
        inputCtrl.clear();
      }
    });

    memberInfoChangedSub = imLogic.memberInfoChangedSubject.listen((info) {
      if (info.groupID == groupID) {
        if (info.userID == OpenIM.iMManager.userID) {
          groupMemberRoleLevel.value = info.roleLevel ?? GroupRoleLevel.member;
          groupMembersInfo = info;
          ();
        }
        _putMemberInfo([info]);

        final index = ownerAndAdmin.indexWhere((element) => element.userID == info.userID);
        if (info.roleLevel == GroupRoleLevel.member) {
          if (index > -1) {
            ownerAndAdmin.removeAt(index);
          }
        } else if (info.roleLevel == GroupRoleLevel.admin || info.roleLevel == GroupRoleLevel.owner) {
          if (index == -1) {
            ownerAndAdmin.add(info);
          } else {
            ownerAndAdmin[index] = info;
          }
        }

        for (var msg in messageList) {
          if (msg.sendID == info.userID) {
            if (msg.isNotificationType) {
              final map = json.decode(msg.notificationElem!.detail!);
              final ntf = GroupNotification.fromJson(map);
              ntf.opUser?.nickname = info.nickname;
              ntf.opUser?.faceURL = info.faceURL;
              msg.notificationElem?.detail = jsonEncode(ntf);
            } else {
              msg.senderFaceUrl = info.faceURL;
              msg.senderNickname = info.nickname;
            }
          }
        }

        messageList.refresh();
      }
    });

    groupInfoUpdatedSub = imLogic.groupInfoUpdatedSubject.listen((value) {
      if (groupID == value.groupID) {
        groupInfo = value;
        nickname.value = value.groupName ?? '';
        faceUrl.value = value.faceURL ?? '';
        memberCount.value = value.memberCount ?? 0;
                // 从群组的ex字段中读取群人数显示设置
        if (value.ex != null && value.ex!.isNotEmpty) {
          try {
            final exData = jsonDecode(value.ex!);
            if (exData.containsKey('showGroupMemberCount')) {
              showGroupMemberCount.value = exData['showGroupMemberCount'] ?? true;
            }
          } catch (e) {
            print('Error parsing group ex data in ChatLogic: $e');
          }
        }
      }
    });

    friendInfoChangedSub = imLogic.friendInfoChangedSubject.listen((value) {
      if (userID == value.userID) {
        nickname.value = value.getShowName();
        faceUrl.value = value.faceURL ?? '';

        for (var msg in messageList) {
          if (msg.sendID == value.userID) {
            msg.senderFaceUrl = value.faceURL;
            msg.senderNickname = value.nickname;
          }
        }

        messageList.refresh();
      }
    });

    selfInfoUpdatedSub = imLogic.selfInfoUpdatedSubject.listen((value) {
      for (var msg in messageList) {
        if (msg.sendID == value.userID) {
          msg.senderFaceUrl = value.faceURL;
          msg.senderNickname = value.nickname;
        }
      }

      messageList.refresh();
    });

    inputCtrl.addListener(() {
      sendTypingMsg(focus: true);
      if (_debounce?.isActive ?? false) _debounce?.cancel();

      _debounce = Timer(1.seconds, () {
        sendTypingMsg(focus: false);
      });
    });

    focusNode.addListener(() {
      focusNodeChanged(focusNode.hasFocus);
    });

    imLogic.onSignalingMessage = (value) {
      if (value.userID == userID) {
        messageList.add(value.message);
        scrollBottom();
      }
    };

     // 监听消息发送进度
    onMsgSendProgress = (msgId, progress) {
      sendProgressSub.addSafely(MsgStreamEv<int>(
        id: msgId,
        value: progress,
      ));
    };

    super.onInit();
  }

  Future chatSetup() async {
    print('chatSetup方法被调用了');
    log('chatSetup方法被调用了');
    final result = isSingleChat
        ? await AppNavigator.startChatSetup(conversationInfo: conversationInfo)
        : await AppNavigator.startGroupChatSetup(conversationInfo: conversationInfo);
    
    // 处理聊天设置页面返回的结果
    if (result != null && result is Map<String, dynamic>) {
      final action = result['action'];
      
      if (action == 'jumpToMessage') {
        final message = result['message'] as Message?;
        
        if (message != null) {
          // 直接在当前聊天页面定位到消息
          searchMessage = message;
          _scrollToSearchMessage();
        }
      }
    }
  }

  void _putMemberInfo(List<GroupMembersInfo>? list) {
    list?.forEach((member) {
      memberUpdateInfoMap[member.userID!] = member;
    });

    messageList.refresh();
  }

  void sendTextMsg() async {
    var content = IMUtils.safeTrim(inputCtrl.text);
    if (content.isEmpty) return;

    Message message;
    
    // 检查是否有引用消息
    if (quotedMessage.value != null) {
      try {
        // 尝试使用 createAdvancedQuoteMessage 创建引用消息
        message = await OpenIM.iMManager.messageManager.createAdvancedQuoteMessage(
          text: content,
          quoteMsg: quotedMessage.value!,
        );
      } catch (e) {
        try {
          // 如果上面的方法不工作，尝试其他可能的方法
          message = await OpenIM.iMManager.messageManager.createTextMessage(
            text: content,
          );
          // 手动设置引用信息（如果 SDK 支持）
          // 注意：这部分可能需要根据实际的 SDK API 进行调整
        } catch (e2) {
          // 最后的备选方案：创建普通文本消息
          message = await OpenIM.iMManager.messageManager.createTextMessage(
            text: content,
          );
          print('Failed to create quote message: $e, $e2');
        }
      }
      
      // 清除引用状态
      clearQuoteMessage();
    } else {
      // 创建普通文本消息
      message = await OpenIM.iMManager.messageManager.createTextMessage(
        text: content,
      );
    }

    _sendMessage(message);
  }

  Future sendPicture({required String path, bool sendNow = true}) async {
    final file = await IMUtils.compressImageAndGetFile(File(path));

    var message = await OpenIM.iMManager.messageManager.createImageMessageFromFullPath(
      imagePath: file!.path,
    );

    if (sendNow) {
      return _sendMessage(message);
    } else {
      // messageList.add(message);
      tempMessages.add(message);
    }
  }

  sendForwardRemarkMsg(
    String content, {
    String? userId,
    String? groupId,
  }) async {
    final message = await OpenIM.iMManager.messageManager.createTextMessage(
      text: content,
    );
    _sendMessage(message, userId: userId, groupId: groupId);
  }

  sendForwardMsg(
    Message originalMessage, {
    String? userId,
    String? groupId,
  }) async {
    var message = await OpenIM.iMManager.messageManager.createForwardMessage(
      message: originalMessage,
    );
    _sendMessage(message, userId: userId, groupId: groupId);
  }

  void sendTypingMsg({bool focus = false}) async {
    if (isSingleChat) {
      OpenIM.iMManager.conversationManager
          .changeInputStates(conversationID: conversationInfo.conversationID, focus: focus);
    }
  }

  void sendCarte({
    required String userID,
    String? nickname,
    String? faceURL,
  }) async {
    var message = await OpenIM.iMManager.messageManager.createCardMessage(
      userID: userID,
      nickname: nickname!,
      faceURL: faceURL,
    );
    _sendMessage(message);
  }

  void sendCustomMsg({
    required String data,
    required String extension,
    required String description,
  }) async {
    var message = await OpenIM.iMManager.messageManager.createCustomMessage(
      data: data,
      extension: extension,
      description: description,
    );
    _sendMessage(message);
  }

  Future _sendMessage(
    Message message, {
    String? userId,
    String? groupId,
    bool addToUI = true,
  }) {
    debugPrint("准备发送消息 - 类型: ${message.contentType}");
    userId = IMUtils.emptyStrToNull(userId);
    groupId = IMUtils.emptyStrToNull(groupId);
    if (null == userId && null == groupId ||
        userId == userID && userId != null ||
        groupId == groupID && groupId != null) {
      if (addToUI) {
        messageList.add(message);
        scrollBottom();
      }
    }
    Logger.print('uid:$userID userId:$userId gid:$groupID groupId:$groupId');
    _reset(message);
    bool useOuterValue = null != userId || null != groupId;

    final recvUserID = useOuterValue ? userId : userID;
    message.recvID = recvUserID;

    return OpenIM.iMManager.messageManager
        .sendMessage(
          message: message,
          userID: recvUserID,
          groupID: useOuterValue ? groupId : groupID,
          offlinePushInfo: Config.offlinePushInfo,
        )
        .then((value) => _sendSucceeded(message, value))
        .catchError((error, _) => _senFailed(message, groupId, userId, error, _))
        .whenComplete(() => _completed());
  }

  void _sendSucceeded(Message oldMsg, Message newMsg) {
    Logger.print('message send success----');
    oldMsg.update(newMsg);
    sendStatusSub.addSafely(MsgStreamEv<bool>(
      id: oldMsg.clientMsgID!,
      value: true,
    ));
  }

  void _senFailed(Message message, String? groupId, String? userId, error, stack) async {
    Logger.print('message send failed userID: $userId groupId:$groupId, catch error :$error  $stack');
    message.status = MessageStatus.failed;
    sendStatusSub.addSafely(MsgStreamEv<bool>(
      id: message.clientMsgID!,
      value: false,
    ));
    if (error is PlatformException) {
      int code = int.tryParse(error.code) ?? 0;
      if (isSingleChat) {
        int? customType;
        if (code == SDKErrorCode.hasBeenBlocked) {
          customType = CustomMessageType.blockedByFriend;
        } else if (code == SDKErrorCode.notFriend) {
          customType = CustomMessageType.deletedByFriend;
        }
        if (null != customType) {
          final hintMessage = (await OpenIM.iMManager.messageManager.createFailedHintMessage(type: customType))
            ..status = 2
            ..isRead = true;
          if (userId != null) {
            if (userId == userID) {
              messageList.add(hintMessage);
            }
          } else {
            messageList.add(hintMessage);
          }
          OpenIM.iMManager.messageManager.insertSingleMessageToLocalStorage(
            message: hintMessage,
            receiverID: userId ?? userID,
            senderID: OpenIM.iMManager.userID,
          );
        }
      } else {
        if ((code == SDKErrorCode.userIsNotInGroup || code == SDKErrorCode.groupDisbanded) && null == groupId) {
          final status = groupInfo?.status;
          final hintMessage = (await OpenIM.iMManager.messageManager.createFailedHintMessage(
              type: status == 2 ? CustomMessageType.groupDisbanded : CustomMessageType.removedFromGroup))
            ..status = 2
            ..isRead = true;
          messageList.add(hintMessage);
          OpenIM.iMManager.messageManager.insertGroupMessageToLocalStorage(
            message: hintMessage,
            groupID: groupID,
            senderID: OpenIM.iMManager.userID,
          );
        }
      }
    }
  }

  void _reset(Message message) {
      // 对于文本消息和引用消息都清空输入框
    if (message.contentType == MessageType.text || message.contentType == MessageType.quote) {
      inputCtrl.clear();
    }
  }

  void _completed() {
    messageList.refresh();
  }

  void markMessageAsRead(Message message, bool visible) async {
    Logger.print('markMessageAsRead: ${message.textElem?.content}, $visible');
    if (visible && message.contentType! < 1000 && message.contentType! != MessageType.voice) {
      var data = IMUtils.parseCustomMessage(message);
      if (null != data && data['viewType'] == CustomMessageType.call) {
        Logger.print('markMessageAsRead: call message $data');
        return;
      }
      _markMessageAsRead(message);
    }
  }

  _markMessageAsRead(Message message) async {
    if (!message.isRead! && message.sendID != OpenIM.iMManager.userID) {
      try {
        Logger.print('mark conversation message as read：${message.clientMsgID!} ${message.isRead}');
        await OpenIM.iMManager.conversationManager
            .markConversationMessageAsRead(conversationID: conversationInfo.conversationID);
      } catch (e) {
        Logger.print('failed to send group message read receipt： ${message.clientMsgID} ${message.isRead}');
      } finally {
        message.isRead = true;
        message.hasReadTime = _timestamp;
        messageList.refresh();
      }
    }
  }

  _clearUnreadCount() {
    if (conversationInfo.unreadCount > 0) {
      OpenIM.iMManager.conversationManager
          .markConversationMessageAsRead(conversationID: conversationInfo.conversationID);
    }
  }

  void closeToolbox() {
    forceCloseToolbox.addSafely(true);
  }

  void onTapAlbum() async {
    final List<AssetEntity>? assets = await AssetPicker.pickAssets(Get.context!,
        pickerConfig: AssetPickerConfig(
            sortPathsByModifiedDate: true,
            filterOptions: PMFilter.defaultValue(containsPathModified: true),
            selectPredicate: (_, entity, isSelected) async {
              if (entity.type == AssetType.image) {
                if (await allowSendImageType(entity)) {
                  return true;
                }

                IMViews.showToast(StrRes.supportsTypeHint);

                return false;
              }

              if (entity.videoDuration > const Duration(seconds: 5 * 60)) {
                IMViews.showToast(sprintf(StrRes.selectVideoLimit, [5]) + StrRes.minute);
                return false;
              }
              return true;
            }));
    if (null != assets) {
      for (var asset in assets) {
        await _handleAssets(asset, sendNow: false);
      }

      // 先将所有消息添加到UI
      for (var msg in tempMessages) {
        messageList.add(msg);
      }
      scrollBottom();

      // 然后发送消息，但不重复添加到UI
      for (var msg in tempMessages) {
        await _sendMessage(msg, addToUI: false);
      }

      tempMessages.clear();
    }
  }
  
  Future<bool> allowSendImageType(AssetEntity entity) async {
    final mimeType = await entity.mimeTypeAsync;

    return IMUtils.allowImageType(mimeType);
  }

  Future _handleAssets(AssetEntity? asset, {bool sendNow = true}) async {
    if (null != asset) {
      Logger.print('--------assets type-----${asset.type} create time: ${asset.createDateTime}');
      final originalFile = await asset.file;
      final originalPath = originalFile!.path;
      var path = originalPath.toLowerCase().endsWith('.gif') ? originalPath : originalFile.path;
      Logger.print('--------assets path-----$path');
      switch (asset.type) {
        case AssetType.image:
          await sendPicture(path: path, sendNow: sendNow);
          break;
        case AssetType.video:
          await sendVideo(path: path, sendNow: sendNow);
          break;
        default:
          break;
      }
      if (Platform.isIOS) {
        originalFile.deleteSync();
      }
    }
  }

  void onTapDirectionalMessage() async {
    if (null != groupInfo) {
      final list = await AppNavigator.startGroupMemberList(
        groupInfo: groupInfo!,
        opType: GroupMemberOpType.call,
      );
      if (list is List<GroupMembersInfo>) {
        directionalUsers.assignAll(list);
      }
    }
  }

  TextSpan? directionalText() {
    if (directionalUsers.isNotEmpty) {
      final temp = <TextSpan>[];

      for (var e in directionalUsers) {
        final r = TextSpan(
          text: '${e.nickname ?? ''} ${directionalUsers.last == e ? '' : ','} ',
          style: Styles.ts_0089FF_14sp,
        );

        temp.add(r);
      }

      return TextSpan(
        text: '${StrRes.directedTo}:',
        style: Styles.ts_8E9AB0_14sp,
        children: temp,
      );
    }

    return null;
  }

  void onClearDirectional() {
    directionalUsers.clear();
  }

  void parseClickEvent(Message msg) async {
    log('parseClickEvent:${jsonEncode(msg)}');
    if (msg.contentType == MessageType.custom) {
      var data = msg.customElem!.data;
      var map = json.decode(data!);
      var customType = map['customType'];
      
      // 处理通话记录点击事件（包括信令消息）
      if ((CustomMessageType.call == customType || 
           CustomMessageType.callingInvite == customType ||
           CustomMessageType.callingAccept == customType ||
           CustomMessageType.callingReject == customType ||
           CustomMessageType.callingCancel == customType ||
           CustomMessageType.callingHungup == customType) && 
          !isInBlacklist.value) {
        _handleCallRecordClick(map);
        return;
      }
    }

    IMUtils.parseClickEvent(
      msg,
      onViewUserInfo: (userInfo) {
        viewUserInfo(userInfo, isCard: msg.isCardType);
      },
      onDownloadProgress: (msgId, progress) {
        // 发送下载进度到进度流
        sendProgressSub.addSafely(MsgStreamEv<int>(
          id: msgId,
          value: progress,
        ));
      },
    );
  }

  // 处理通话记录点击事件
  void _handleCallRecordClick(Map<String, dynamic> callData) {
    if (rtcIsBusy) {
      IMViews.showToast(StrRes.callingBusy);
      return;
    }

    // 检查对方是否在黑名单中
    if (isInBlacklist.value) {
      IMViews.showToast(StrRes.cannotCallBlacklistedUser);
      return;
    }

    // 从通话记录数据中获取通话类型
    final data = callData['data'];
    if (data != null) {
      String? callType;
      
      // 根据消息类型确定通话类型
      final customType = callData['customType'];
      if (customType == CustomMessageType.call) {
        // 通话记录消息，从 data 中获取 type
        callType = data['type']; // 'audio' 或 'video'
      } else {
        // 信令消息，从 data 中获取 mediaType
        callType = data['mediaType']; // 'video' 或 'audio'
        // 将 video/audio 转换为对应的类型
        if (callType == 'video') {
          callType = 'video';
        } else {
          callType = 'audio';
        }
      }
      
      // 根据通话类型直接发起相应的通话
      if (callType == 'audio') {
        // 发起语音通话
        imLogic.call(
          callObj: CallObj.single,
          callType: CallType.audio,
          inviteeUserIDList: [if (isSingleChat) userID!],
        );
      } else if (callType == 'video') {
        // 发起视频通话
        imLogic.call(
          callObj: CallObj.single,
          callType: CallType.video,
          inviteeUserIDList: [if (isSingleChat) userID!],
        );
      }
    }
  }

  void onTapLeftAvatar(Message message) {
    viewUserInfo(UserInfo()
      ..userID = message.sendID
      ..nickname = message.senderNickname
      ..faceURL = message.senderFaceUrl);
  }

  void onTapRightAvatar() {
    viewUserInfo(OpenIM.iMManager.userInfo);
  }

  void viewUserInfo(UserInfo userInfo, {bool isCard = false}) {
    if (isGroupChat && !isAdminOrOwner && !isCard) {
      if (groupInfo!.lookMemberInfo != 1) {
        AppNavigator.startUserProfilePane(
          userID: userInfo.userID!,
          nickname: userInfo.nickname,
          faceURL: userInfo.faceURL,
          groupID: groupID,
          offAllWhenDelFriend: isSingleChat,
        );
      }
    } else {
      AppNavigator.startUserProfilePane(
        userID: userInfo.userID!,
        nickname: userInfo.nickname,
        faceURL: userInfo.faceURL,
        groupID: groupID,
        offAllWhenDelFriend: isSingleChat,
        forceCanAdd: isCard,
      );
    }
  }

  void clickLinkText(url, type) async {
    final uri = Uri.tryParse(url.toString());
    if (uri != null) {
      final postId = _extractPostIdFromUri(uri);
      if (postId != null) {
        AppNavigator.startPostDetail(postId);
        return;
      }
    }
    if (await canLaunch(url)) {
      await launch(url);
    }
  }

  String? _extractPostIdFromUri(Uri uri) {
    if (uri.pathSegments.length >= 2 && uri.pathSegments.first == 'posts') {
      final idSeg = uri.pathSegments[1];
      final id = int.tryParse(idSeg);
      if (id != null) return idSeg;
    }
    final reg = RegExp(r'/posts/(\d+)');
    final m = reg.firstMatch(uri.toString());
    if (m != null) return m.group(1);
    return null;
  }

  exit() async {
    Get.back();

    return true;
  }

  void focusNodeChanged(bool hasFocus) {
    if (hasFocus) {
      Logger.print('focus:$hasFocus');
      scrollBottom();
    }
  }

  Message indexOfMessage(int index, {bool calculate = true}) => IMUtils.calChatTimeInterval(
        messageList,
        calculate: calculate,
      ).reversed.elementAt(index);

  ValueKey itemKey(Message message) => ValueKey(message.clientMsgID!);

  @override
  void onClose() {
    sendTypingMsg();
    _clearUnreadCount();
    inputCtrl.dispose();
    focusNode.dispose();
    forceCloseToolbox.close();
    conversationSub.cancel();
    sendStatusSub.close();
    memberAddSub.cancel();
    memberDelSub.cancel();
    memberInfoChangedSub.cancel();
    groupInfoUpdatedSub.cancel();
    friendInfoChangedSub.cancel();
    userStatusChangedSub?.cancel();
    selfInfoUpdatedSub?.cancel();
    joinedGroupAddedSub.cancel();
    joinedGroupDeletedSub.cancel();
    connectionSub.cancel();

    _debounce?.cancel();
    sendProgressSub.close();
    super.onClose();
  }

  String? getShowTime(Message message) {
    if (message.exMap['showTime'] == true) {
      return IMUtils.getChatTimeline(message.sendTime!);
    }
    return null;
  }

  void clearAllMessage() {
    messageList.clear();
  }

  void _initChatConfig() async {
    scaleFactor.value = DataSp.getChatFontSizeFactor();
    var path = DataSp.getChatBackground(otherId) ?? '';
    if (path.isNotEmpty && (await File(path).exists())) {
      background.value = path;
    }
     // 初始化群人数显示设置
    if (isGroupChat) {
            // 优先从群组的ex字段读取设置
      if (groupInfo?.ex != null && groupInfo!.ex!.isNotEmpty) {
        try {
          final exData = jsonDecode(groupInfo!.ex!);
          if (exData.containsKey('showGroupMemberCount')) {
            showGroupMemberCount.value = exData['showGroupMemberCount'] ?? true;
          } else {
            // 如果ex字段中没有设置，使用本地存储的默认值
            showGroupMemberCount.value = DataSp.getShowGroupMemberCount();
          }
        } catch (e) {
          // 解析失败时使用本地存储的默认值
          showGroupMemberCount.value = DataSp.getShowGroupMemberCount();
        }
      } else {
        // 如果没有ex字段，使用本地存储的默认值
        showGroupMemberCount.value = DataSp.getShowGroupMemberCount();
      }
    }
  }

  void _preloadGroupMemberFriendRemarks() async {
    try {
      if (groupID != null) {
        final members = await OpenIM.iMManager.groupManager.getGroupMemberList(
          groupID: groupID!,
          count: 1000,
        );
        await IMUtils.preloadGroupMemberRemarks(members);
      }
    } catch (e) {
      print('预加载群成员好友备注失败: $e');
    }
  }

  void _preloadGroupMemberDomainAvatars() async {
    try {
      if (groupID != null) {
        final members = await OpenIM.iMManager.groupManager.getGroupMemberList(
          groupID: groupID!,
          count: 1000,
        );
        final tasks = <Future<void>>[];
        for (final m in members) {
          final uid = m.userID;
          if (uid != null && uid.isNotEmpty) {
            final cached = UserCacheManager().getUserInfo(uid);
            if (cached != null) {
              final face = cached.faceURL;
              final nick = cached.nickname;
              if (face != null && face.isNotEmpty) memberDomainAvatar[uid] = face;
              if (nick != null && nick.isNotEmpty) memberDomainNickname[uid] = nick;
              continue;
            }
            tasks.add(() async {
              try {
                final profile = await Apis.getUserProfileByOpenIMID(uid);
                if (profile != null) {
                  if (profile.faceURL != null && profile.faceURL!.isNotEmpty) {
                    memberDomainAvatar[uid] = profile.faceURL!;
                  }
                  if (profile.nickname != null && profile.nickname!.isNotEmpty) {
                    memberDomainNickname[uid] = profile.nickname!;
                  }
                  UserCacheManager().addOrUpdateUserInfo(uid, profile);
                }
              } catch (_) {}
            }());
          }
        }
        await Future.wait(tasks);
        isGroupDomainAvatarsReady.value = true;
      }
    } catch (_) {}
  }
  void updateShowGroupMemberCount(bool show) {
    if (isGroupChat) {
      showGroupMemberCount.value = show;
    }
  }
  String get otherId => isSingleChat ? userID! : groupID!;

  void failedResend(Message message) {
    Logger.print('failedResend: ${message.clientMsgID}');
    if (message.status == MessageStatus.sending) {
      return;
    }
    sendStatusSub.addSafely(MsgStreamEv<bool>(
      id: message.clientMsgID!,
      value: true,
    ));

    Logger.print('failedResending: ${message.clientMsgID}');
    _sendMessage(message..status = MessageStatus.sending, addToUI: false);
  }

  static int get _timestamp => DateTime.now().millisecondsSinceEpoch;

  void destroyMsg() {
    for (var message in privateMessageList) {
      OpenIM.iMManager.messageManager.deleteMessageFromLocalAndSvr(
        conversationID: conversationInfo.conversationID,
        clientMsgID: message.clientMsgID!,
      );
    }
  }

  Future _queryMyGroupMemberInfo() async {
    if (!isGroupChat) {
      return;
    }
    var list = await OpenIM.iMManager.groupManager.getGroupMembersInfo(
      groupID: groupID!,
      userIDList: [OpenIM.iMManager.userID],
    );
    groupMembersInfo = list.firstOrNull;
    groupMemberRoleLevel.value = groupMembersInfo?.roleLevel ?? GroupRoleLevel.member;
    if (null != groupMembersInfo) {
      memberUpdateInfoMap[OpenIM.iMManager.userID] = groupMembersInfo!;
    }

    return;
  }

  Future _queryOwnerAndAdmin() async {
    if (isGroupChat) {
      ownerAndAdmin = await OpenIM.iMManager.groupManager.getGroupMemberList(groupID: groupID!, filter: 5, count: 20);
    }
    return;
  }

  void _isJoinedGroup() async {
    if (!isGroupChat) {
      return;
    }
    isInGroup.value = await OpenIM.iMManager.groupManager.isJoinedGroup(
      groupID: groupID!,
    );
    if (!isInGroup.value) {
      return;
    }
    _queryGroupInfo();
    _queryOwnerAndAdmin();
  }

  void _queryGroupInfo() async {
    if (!isGroupChat) {
      return;
    }
    var list = await OpenIM.iMManager.groupManager.getGroupsInfo(
      groupIDList: [groupID!],
    );
    groupInfo = list.firstOrNull;
    groupOwnerID = groupInfo?.ownerUserID;
    if (null != groupInfo?.memberCount) {
      memberCount.value = groupInfo!.memberCount!;
    }
        // 从群组的ex字段中读取群人数显示设置
    if (groupInfo?.ex != null && groupInfo!.ex!.isNotEmpty) {
      try {
        final exData = jsonDecode(groupInfo!.ex!);
        if (exData.containsKey('showGroupMemberCount')) {
          showGroupMemberCount.value = exData['showGroupMemberCount'] ?? true;
        }
      } catch (e) {
        print('Error parsing group ex data in _queryGroupInfo: $e');
      }
    }
    _queryMyGroupMemberInfo();
  }

  bool get havePermissionMute =>
      isGroupChat &&
      (groupInfo?.ownerUserID == OpenIM.iMManager.userID /*||
          groupMembersInfo?.roleLevel == 2*/
      );

  bool isNotificationType(Message message) => message.contentType! >= 1000;

  Map<String, String> getAtMapping(Message message) {
    return {};
  }

  void _checkInBlacklist() async {
    if (userID != null) {
      var list = await OpenIM.iMManager.friendshipManager.getBlacklist();
      var user = list.firstWhereOrNull((e) => e.userID == userID);
      isInBlacklist.value = user != null;
    }
  }

  bool isExceed24H(Message message) {
    int milliseconds = message.sendTime!;
    return !DateUtil.isToday(milliseconds);
  }

  String? getNewestNickname(Message message) {
    if (isSingleChat) return null;
    if (!isGroupDomainAvatarsReady.value) return '';
    final uid = message.sendID;
    if (uid != null) {
      final domainNick = memberDomainNickname[uid];
      if (domainNick != null && domainNick.isNotEmpty) return domainNick;
    }
    return message.senderNickname;
  }

  String? getNewestFaceURL(Message message) {
    if (isSingleChat) {
      final url = faceUrl.value;
      return url.isNotEmpty ? url : conversationInfo.faceURL;
    }
    if (!isGroupDomainAvatarsReady.value) return '';
    final uid = message.sendID;
    if (uid != null) {
      final v = memberDomainAvatar[uid];
      if (v != null && v.isNotEmpty) return v;
    }
    return '';
  }

  bool get isInvalidGroup => !isInGroup.value && isGroupChat;

  void _resetGroupAtType() {
    if (conversationInfo.groupAtType != GroupAtType.atNormal) {
      OpenIM.iMManager.conversationManager.resetConversationGroupAtType(
        conversationID: conversationInfo.conversationID,
      );
    }
  }

  WillPopCallback? willPop() {
    return null;
  }

  void call() {
    if (rtcIsBusy) {
      IMViews.showToast(StrRes.callingBusy);
      return;
    }

    // 检查对方是否在黑名单中
    if (isInBlacklist.value) {
      IMViews.showToast(StrRes.cannotCallBlacklistedUser);
      return;
    }

    IMViews.openIMCallSheet(nickname.value, (index) {
      imLogic.call(
        callObj: CallObj.single,
        callType: index == 0 ? CallType.audio : CallType.video,
        inviteeUserIDList: [if (isSingleChat) userID!],
      );
    });
  }

  void onScrollToTop() {
    if (scrollingCacheMessageList.isNotEmpty) {
      messageList.addAll(scrollingCacheMessageList);
      scrollingCacheMessageList.clear();
    }
  }

  String get markText {
    String? phoneNumber = imLogic.userInfo.value.phoneNumber;
    if (phoneNumber != null) {
      int start = phoneNumber.length > 4 ? phoneNumber.length - 4 : 0;
      final sub = phoneNumber.substring(start);
      return "${OpenIM.iMManager.userInfo.nickname!}$sub";
    }
    return OpenIM.iMManager.userInfo.nickname ?? '';
  }

  bool isFailedHintMessage(Message message) {
    if (message.contentType == MessageType.custom) {
      var data = message.customElem!.data;
      var map = json.decode(data!);
      var customType = map['customType'];
      return customType == CustomMessageType.deletedByFriend || customType == CustomMessageType.blockedByFriend;
    }
    return false;
  }

  void sendFriendVerification() => AppNavigator.startSendVerificationApplication(userID: userID);

  void _setSdkSyncDataListener() {
    connectionSub = imLogic.imSdkStatusPublishSubject.listen((value) {
      syncStatus.value = value.status;
      if (value.status == IMSdkStatus.syncStart) {
        _isStartSyncing = true;
      } else if (value.status == IMSdkStatus.syncEnded) {
        if (/*_isReceivedMessageWhenSyncing &&*/ _isStartSyncing) {
          _isReceivedMessageWhenSyncing = false;
          _isStartSyncing = false;
          _isFirstLoad = true;
          _loadHistoryForSyncEnd();
        }
      } else if (value.status == IMSdkStatus.syncFailed) {
        _isReceivedMessageWhenSyncing = false;
        _isStartSyncing = false;
      }
    });
  }

  bool get isSyncFailed => syncStatus.value == IMSdkStatus.syncFailed;

  String? get syncStatusStr {
    switch (syncStatus.value) {
      case IMSdkStatus.syncStart:
      case IMSdkStatus.synchronizing:
        return StrRes.synchronizing;
      case IMSdkStatus.syncFailed:
        return StrRes.syncFailed;
      default:
        return null;
    }
  }

  bool showBubbleBg(Message message) {
    return !isNotificationType(message) && !isFailedHintMessage(message);
  }

  Future<AdvancedMessage> _fetchHistoryMessages() {
    Logger.print(
        '_fetchHistoryMessages: is first load: $_isFirstLoad, last client id: ${_isFirstLoad ? null : messageList.firstOrNull?.clientMsgID}');
    return OpenIM.iMManager.messageManager.getAdvancedHistoryMessageList(
      conversationID: conversationInfo.conversationID,
      count: _pageSize,
      startMsg: _isFirstLoad ? null : messageList.firstOrNull,
    );
  }

  Future<bool> onScrollToBottomLoad() async {
    late List<Message> list;
    final result = await _fetchHistoryMessages();
    if (result.messageList == null || result.messageList!.isEmpty) {
      _getGroupInfoAfterLoadMessage();

      return false;
    }
    list = result.messageList!;
    if (_isFirstLoad) {
      _isFirstLoad = false;
      // remove the message that has been timed down
      messageList.assignAll(list);
     
      // 如果有搜索消息，定位到该消息，否则滚动到底部
      if (searchMessage != null) {
        _scrollToSearchMessage();
      } else {
        scrollBottom();
      }

      _getGroupInfoAfterLoadMessage();
    } else {
      messageList.insertAll(0, list);
    }

    return result.isEnd != true;
  }

  // 改进的滚动到搜索消息方法（基于相对位置）
void _scrollToSearchMessage() async {
  if (searchMessage == null) return;
  
  final targetIndex = messageList.indexWhere((msg) => 
    msg.clientMsgID == searchMessage!.clientMsgID || 
    msg.serverMsgID == searchMessage!.serverMsgID
  );
  
  if (targetIndex != -1) {
    // 计算目标消息在列表中的相对位置（0-1）
    final relativePosition = targetIndex / messageList.length;
    
    // 等待布局完成
    await Future.delayed(const Duration(milliseconds: 100));
    
    // 获取可滚动区域的总高度
    final maxScrollExtent = scrollController.position.maxScrollExtent;
    
    // 计算目标滚动位置
    final targetOffset = maxScrollExtent * (1 - relativePosition);
    
    // 滚动到目标位置
    scrollController.animateTo(
      targetOffset.clamp(0.0, maxScrollExtent),
      duration: const Duration(milliseconds: 500),
      curve: Curves.easeInOut,
    );
    
    // 延迟后进行微调（如果需要）
    Future.delayed(const Duration(milliseconds: 600), () {
      _finetuneScrollPosition(targetIndex);
    });
    
    IMViews.showToast('已定位到指定消息');
  } else {
    await _loadMoreHistoryToFindMessage();
  }
}

// 微调滚动位置的方法
void _finetuneScrollPosition(int targetIndex) {
  // 基于消息类型进行高度估算的微调
  final targetMessage = messageList[targetIndex];
  double estimatedHeight = _estimateMessageHeight(targetMessage);
  
  // 根据消息类型调整滚动位置
  final currentOffset = scrollController.offset;
  final adjustment = (estimatedHeight - 80.0) * 0.5; // 基于默认高度的差异进行调整
  
  if (adjustment.abs() > 10) { // 只有差异较大时才调整
    scrollController.animateTo(
      (currentOffset + adjustment).clamp(0.0, scrollController.position.maxScrollExtent),
      duration: const Duration(milliseconds: 200),
      curve: Curves.easeOut,
    );
  }
}

// 根据消息类型估算高度
double _estimateMessageHeight(Message message) {
  switch (message.contentType) {
    case MessageType.text:
      // 根据文本长度估算高度
      final textLength = message.textElem?.content?.length ?? 0;
      final lines = (textLength / 30).ceil(); // 假设每行30个字符
      return 60.0 + (lines - 1) * 20.0; // 基础高度 + 额外行高
    
    case MessageType.picture:
      return 200.0; // 图片消息固定高度
    
    case MessageType.voice:
      return 80.0; // 语音消息固定高度
    
    case MessageType.video:
      return 200.0; // 视频消息固定高度
    
    case MessageType.file:
      return 100.0; // 文件消息固定高度
    
    default:
      return 80.0; // 默认高度
  }
}

  // 加载更多历史消息来查找目标消息
  Future<void> _loadMoreHistoryToFindMessage() async {
    if (searchMessage == null) return;
    
    try {
      // 从搜索消息的时间点开始加载历史消息
      final result = await OpenIM.iMManager.messageManager.getAdvancedHistoryMessageList(
        conversationID: conversationInfo.conversationID,
        startMsg: searchMessage,
        count: 50,
      );
      
      if (result.messageList?.isNotEmpty == true) {
        // 将新加载的消息插入到列表开头
        messageList.insertAll(0, result.messageList!);
        
        // 再次尝试定位到搜索消息
        _scrollToSearchMessage();
      } else {
        IMViews.showToast('未找到指定消息');
        scrollBottom(); // 如果找不到，就滚动到底部
      }
    } catch (e) {
      print('加载历史消息失败: $e');
      IMViews.showToast('定位消息失败');
      scrollBottom();
    }
  }

  Future<void> _loadHistoryForSyncEnd() async {
    final result = await OpenIM.iMManager.messageManager.getAdvancedHistoryMessageList(
      conversationID: conversationInfo.conversationID,
      count: messageList.length < _pageSize ? _pageSize : messageList.length,
      startMsg: null,
    );
    if (result.messageList == null || result.messageList!.isEmpty) return;
    final list = result.messageList!;

    final offset = scrollController.offset;
    messageList.assignAll(list);
    scrollController.jumpTo(offset);
  }

  void _getGroupInfoAfterLoadMessage() {
    if (isGroupChat && ownerAndAdmin.isEmpty) {
      _isJoinedGroup();
    } else {
      _checkInBlacklist();
    }
  }

  recommendFriendCarte(UserInfo userInfo) async {
    final result = await AppNavigator.startSelectContacts(
      action: SelAction.recommend,
      ex: '[${StrRes.carte}]${userInfo.nickname}',
      cardUserInfo: userInfo, // 传递完整的用户信息
    );
    if (null != result) {
      final customEx = result['customEx'];
      final checkedList = result['checkedList'];
      for (var info in checkedList) {
        final userID = IMUtils.convertCheckedToUserID(info);
        final groupID = IMUtils.convertCheckedToGroupID(info);
        if (customEx is String && customEx.isNotEmpty) {
          _sendMessage(
            await OpenIM.iMManager.messageManager.createTextMessage(
              text: customEx,
            ),
            userId: userID,
            groupId: groupID,
          );
        }
        _sendMessage(
          await OpenIM.iMManager.messageManager.createCardMessage(
            userID: userInfo.userID!,
            nickname: userInfo.nickname!,
            faceURL: userInfo.faceURL,
          ),
          userId: userID,
          groupId: groupID,
        );
      }
    }
  }

  @override
  void onDetached() {}

  @override
  void onHidden() {}

  @override
  void onInactive() {}

  @override
  void onPaused() {}

  @override
  void onResumed() {
    _loadHistoryForSyncEnd();
  }
   // 菜单相关的回调方法
  void onCopyMessage(Message message) {
    if (message.isTextType && message.textElem?.content != null) {
      Clipboard.setData(ClipboardData(text: message.textElem!.content!));
      IMViews.showToast(StrRes.copySuccessfully);
    }
  }

  void onDeleteMessage(Message message) async {
    try {
      await OpenIM.iMManager.messageManager.deleteMessageFromLocalAndSvr(
        conversationID: conversationInfo.conversationID,
        clientMsgID: message.clientMsgID!,
      );
      messageList.removeWhere((msg) => msg.clientMsgID == message.clientMsgID);
      messageList.refresh();
      IMViews.showToast('消息已删除');
    } catch (e) {
      IMViews.showToast('删除失败: $e');
    }
  }

  void onRevokeMessage(Message message) async {
    try {
      await OpenIM.iMManager.messageManager.revokeMessage(
        conversationID: conversationInfo.conversationID,
        clientMsgID: message.clientMsgID!,
      );
      IMViews.showToast('消息已撤回');
    } catch (e) {
      IMViews.showToast('撤回失败: $e');
    }
  }

  void onForwardMessage(Message message) async {
    final result = await AppNavigator.startSelectContacts(
      action: SelAction.forward,
      ex: '[转发消息]',
      // 传递单个消息到联系人选择页面，供转发对话框显示
      forwardMessages: [message],
    );
    if (null != result) {
      final checkedList = result['checkedList'];
      for (var info in checkedList) {
        final userID = IMUtils.convertCheckedToUserID(info);
        final groupID = IMUtils.convertCheckedToGroupID(info);
        await sendForwardMsg(
          message,
          userId: userID,
          groupId: groupID,
        );
      }
      IMViews.showToast('消息已转发');
    }
  }

  void onMultiSelectMessage(Message message) {
    // 进入多选模式
    isMultiSelectMode.value = true;
    // 添加当前消息到选中列表
    if (!selectedMessages.any((msg) => msg.clientMsgID == message.clientMsgID)) {
      selectedMessages.add(message);
    }
    // 关闭工具箱
    closeToolbox();
    
    // IMViews.showToast('已进入多选模式');
  }
  
  // 切换消息选中状态
  void toggleMessageSelection(Message message) {
    if (!isMultiSelectMode.value) return;
    
    final index = selectedMessages.indexWhere((msg) => msg.clientMsgID == message.clientMsgID);
    if (index >= 0) {
      selectedMessages.removeAt(index);
    } else {
      selectedMessages.add(message);
    }
  }
  // 检查消息是否被选中
  bool isMessageSelected(Message message) {
    return selectedMessages.any((msg) => msg.clientMsgID == message.clientMsgID);
  }
  // 退出多选模式
  void exitMultiSelectMode() {
    isMultiSelectMode.value = false;
    selectedMessages.clear();
  }
  // 全选消息
void selectAllMessages() {
    if (!isMultiSelectMode.value) return;

    // 如果已全选，则清空选中列表
    if (areAllMessagesSelected) {
      selectedMessages.clear();
      return;
    }

    // 否则执行全选
    selectedMessages.clear();
    for (final message in messageList) {
      if (_canSelectMessage(message)) {
        selectedMessages.add(message);
      }
    }
  }
  // 判断消息是否可以被选中
  bool _canSelectMessage(Message message) {
    // 排除系统通知消息
    if (message.contentType! >= 1000) return false;
    
    // 排除一些特殊的自定义消息
    final data = IMUtils.parseCustomMessage(message);
    if (data != null) {
      final customType = data['customType'];
      if (customType == CustomMessageType.blockedByFriend ||
          customType == CustomMessageType.deletedByFriend ||
          customType == CustomMessageType.removedFromGroup ||
          customType == CustomMessageType.groupDisbanded ||
          customType == CustomMessageType.pinMessage ||
          customType == CustomMessageType.unpinMessage) {
        return false;
      }
    }
    
    return true;
  }

  // 批量删除选中的消息
  Future<void> deleteSelectedMessages() async {
    if (selectedMessages.isEmpty) return;
     // 保存选中消息的数量，避免在清空后丢失
    final deleteCount = selectedMessages.length;
    try {
      for (final message in selectedMessages) {
        await OpenIM.iMManager.messageManager.deleteMessageFromLocalAndSvr(
          conversationID: conversationInfo.conversationID,
          clientMsgID: message.clientMsgID!,
        );
        messageList.removeWhere((msg) => msg.clientMsgID == message.clientMsgID);
      }
      
      messageList.refresh();
      exitMultiSelectMode();
      IMViews.showToast('已删除 $deleteCount 条消息');
    } catch (e) {
      IMViews.showToast('删除失败: $e');
    }
  }
  
  Future<void> confirmAndDeleteSelectedMessages() async {
    if (selectedMessages.isEmpty) return;
    final count = selectedMessages.length;
    var confirm = await Get.dialog(CustomDialog(
      title: '确认删除所选消息（$count 条）吗？',
    ));
    if (confirm == true) {
      await deleteSelectedMessages();
    }
  }
  // 批量转发选中的消息
  Future<void> forwardSelectedMessages() async {
    if (selectedMessages.isEmpty) return;
    
    final result = await AppNavigator.startSelectContacts(
      action: SelAction.forward,
      ex: '[转发 ${selectedMessages.length} 条消息]',
      // 新增：把选中的消息传到联系人选择页面，供确认对话框展示详情
      forwardMessages: selectedMessages.toList(),
    );
    
    if (result != null) {
      final checkedList = result['checkedList'];
      
      try {
        for (var info in checkedList) {
          final userID = IMUtils.convertCheckedToUserID(info);
          final groupID = IMUtils.convertCheckedToGroupID(info);
          
          for (final message in selectedMessages) {
            await sendForwardMsg(
              message,
              userId: userID,
              groupId: groupID,
            );
          }
        }
        final message_count = selectedMessages.length;
        exitMultiSelectMode();
        IMViews.showToast('已转发 $message_count 条消息');
      } catch (e) {
        IMViews.showToast('转发失败: $e');
      }
    }
  }

  // 获取选中消息数量
  int get selectedMessageCount => selectedMessages.length;
  
  // 修改 onPinMessage 方法以发送通知消息
void onPinMessage(Message message) async {
  try {
    // 一对一私聊时不允许置顶消息
    if (isSingleChat) {
      return;
    }
    
    final isPinned = pinnedMessages.any((msg) => msg.clientMsgID == message.clientMsgID);
    
    if (isPinned) {
      // 取消置顶
      await _unpinMessage(message);
    } else {
      // 添加置顶
      await _pinMessage(message);
    }
  } catch (e) {
    IMViews.showToast('操作失败: $e');
  }
}

// 置顶消息
Future<void> _pinMessage(Message message) async {
  if (pinnedMessages.length >= 3) {
    IMViews.showToast('最多只能置顶3条消息');
    return;
  }
  
  pinnedMessages.add(message);
  await _savePinnedMessages();
  showPinnedMessages.value = pinnedMessages.isNotEmpty;
  
  // 发送置顶通知消息
  await _sendPinNotificationMessage(message, true);
  
  IMViews.showToast('消息已置顶');
}

// 取消置顶消息
Future<void> _unpinMessage(Message message) async {
  pinnedMessages.removeWhere((msg) => msg.clientMsgID == message.clientMsgID);
  await _savePinnedMessages();
  showPinnedMessages.value = pinnedMessages.isNotEmpty;
  
  // 发送取消置顶通知消息
  await _sendPinNotificationMessage(message, false);
  
  IMViews.showToast('已取消置顶');
}

// 发送置顶通知消息
Future<void> _sendPinNotificationMessage(Message targetMessage, bool isPin) async {
  try {
  final customData = {
      'customType': isPin ? CustomMessageType.pinMessage : CustomMessageType.unpinMessage, // 移到这里
      'type': isPin ? 'pin' : 'unpin',
      'operatorId': OpenIM.iMManager.userID,
      'operatorName': OpenIM.iMManager.userInfo.nickname ?? '',
      'targetMessageId': targetMessage.clientMsgID,
      'targetMessageType': targetMessage.contentType,
      'targetMessageContent': _getMessagePreview(targetMessage),
      'timestamp': DateTime.now().millisecondsSinceEpoch,
    };

    final message = await OpenIM.iMManager.messageManager.createCustomMessage(
      data: jsonEncode(customData),
      extension: jsonEncode({
        'customType': isPin ? CustomMessageType.pinMessage : CustomMessageType.unpinMessage,
      }),
      description: isPin ? '置顶了一条消息' : '取消置顶了一条消息',
    );

    // 直接使用 OpenIM SDK 的 sendMessage 方法
    await OpenIM.iMManager.messageManager.sendMessage(
      message: message,
      userID: isSingleChat ? userID : null,
      groupID: isGroupChat ? groupID : null,
      offlinePushInfo: OfflinePushInfo(
        title: '${OpenIM.iMManager.userInfo.nickname}${isPin ? '置顶' : '取消置顶'}了一条消息',
        desc: _getMessagePreview(targetMessage),
        ex: jsonEncode({'conversationID': conversationInfo.conversationID}),
        iOSPushSound: '+1',
        iOSBadgeCount: true,
      ),
    );
    
    Logger.print('置顶通知消息发送成功: ${isPin ? 'pin' : 'unpin'}');
  } catch (e) {
    Logger.print('发送置顶通知消息失败: $e');
    IMViews.showToast('网络异常，请稍后重试');
  }
}

// 获取消息预览内容
String _getMessagePreview(Message message) {
  switch (message.contentType) {
    case MessageType.text:
      final content = message.textElem?.content ?? '';
      return content.length > 50 ? '${content.substring(0, 50)}...' : content;
    case MessageType.picture:
      return '[图片]';
    case MessageType.video:
      return '[视频]';
    case MessageType.voice:
      return '[语音]';
    case MessageType.file:
      return '[文件] ${message.fileElem?.fileName ?? ''}';
    case MessageType.location:
      return '[位置]';
    case MessageType.card:
      return '[名片] ${message.cardElem?.nickname ?? ''}';
    default:
      return '[消息]';
  }
}


// 处理置顶通知消息
Future<void> _handlePinNotificationMessage(Message notificationMessage, Map<String, dynamic> data, bool isPin) async {
  debugPrint("handlePinNotificationMessage1: $notificationMessage");
  try {
    final operatorId = data['operatorId'] as String?;
    final targetMessageId = data['targetMessageId'] as String?;
    final operatorName = data['operatorName'] as String? ?? '某人';
    
    if (targetMessageId == null) {
      Logger.print('置顶通知消息缺少目标消息ID');
      return;
    }
    
    // 如果是自己的操作，忽略处理（避免重复操作）
    if (operatorId == OpenIM.iMManager.userID) {
      Logger.print('忽略自己的置顶操作通知');
      return;
    }
    
    // 查找目标消息
    Message? targetMessage = messageList.firstWhereOrNull(
      (msg) => msg.clientMsgID == targetMessageId || msg.serverMsgID == targetMessageId
    );
    
    // 如果在当前消息列表中找不到，尝试从历史消息中查找
    if (targetMessage == null) {
      targetMessage = await _findMessageInHistory(targetMessageId);
    }
    
    if (targetMessage != null) {
      if (isPin) {
        // 添加到置顶列表
        if (!pinnedMessages.any((msg) => msg.clientMsgID == targetMessage!.clientMsgID)) {
          // 限制置顶消息数量
          if (pinnedMessages.length >= 3) {
            pinnedMessages.removeAt(0); // 移除最旧的置顶消息
          }
          pinnedMessages.add(targetMessage);
          await _savePinnedMessages();
          showPinnedMessages.value = true;
          
          IMViews.showToast('$operatorName 置顶了一条消息');
        }
      } else{
        // 从置顶列表移除
        final messageToRemove = pinnedMessages.firstWhereOrNull(
          (msg) => msg.clientMsgID == targetMessage!.clientMsgID
        );
        
        if (messageToRemove != null) {
          pinnedMessages.remove(messageToRemove);
          await _savePinnedMessages();
          showPinnedMessages.value = pinnedMessages.isNotEmpty;
          
          IMViews.showToast('$operatorName 取消置顶了一条消息');
        }
      }
    } else {
      Logger.print('未找到目标消息: $targetMessageId');
      // 即使找不到消息，也显示通知
      IMViews.showToast('$operatorName ${isPin ? '置顶' : '取消置顶'}了一条消息');
    }
  } catch (e) {
    Logger.print('处理置顶通知消息失败: $e');
  }
}

  // 从历史消息中查找消息
  Future<Message?> _findMessageInHistory(String messageId) async {
    try {
      
      // 方法2：使用 searchLocalMessages 作为备选方案
      try {
        final searchResult = await OpenIM.iMManager.messageManager.searchLocalMessages(
          conversationID: conversationInfo.conversationID,
          keywordList: [messageId],
          messageTypeList: [], // 搜索所有类型的消息
          pageIndex: 1,
          count: 10,
        );
        
        if (searchResult.searchResultItems?.isNotEmpty == true) {
          for (var item in searchResult.searchResultItems!) {
            if (item.messageList?.isNotEmpty == true) {
              final foundMessage = item.messageList!.firstWhereOrNull(
                (msg) => msg.clientMsgID == messageId || msg.serverMsgID == messageId
              );
              if (foundMessage != null) {
                return foundMessage;
              }
            }
          }
        }
      } catch (e) {
        Logger.print('使用searchLocalMessages查找失败: $e');
      }
      
      // 方法3：通过历史消息列表查找
      final historyResult = await OpenIM.iMManager.messageManager.getAdvancedHistoryMessageList(
        conversationID: conversationInfo.conversationID,
        count: 100,
      );
      
      return historyResult.messageList?.firstWhereOrNull(
        (msg) => msg.clientMsgID == messageId || msg.serverMsgID == messageId
      );
    } catch (e) {
      Logger.print('从历史消息中查找失败: $e');
      return null;
    }
  }

    // 保存置顶消息到本地存储
  Future<void> _savePinnedMessages() async {
    try {
      final pinnedData = pinnedMessages.map((msg) => {
        'clientMsgID': msg.clientMsgID,
        'serverMsgID': msg.serverMsgID,
        'sendID': msg.sendID,
        'recvID': msg.recvID,
        'senderPlatformID': msg.senderPlatformID,
        'senderNickname': msg.senderNickname,
        'senderFaceUrl': msg.senderFaceUrl,
        'groupID': msg.groupID,
        'contentType': msg.contentType,
        'seq': msg.seq,
        'sendTime': msg.sendTime,
        'createTime': msg.createTime,
        'status': msg.status,
        'isRead': msg.isRead,
        'ex': msg.ex,
        // 只保存存在的元素类型
        if (msg.textElem != null) 'textElem': msg.textElem!.toJson(),
        if (msg.pictureElem != null) 'pictureElem': msg.pictureElem!.toJson(),
        if (msg.soundElem != null) 'soundElem': msg.soundElem!.toJson(),
        if (msg.videoElem != null) 'videoElem': msg.videoElem!.toJson(),
        if (msg.fileElem != null) 'fileElem': msg.fileElem!.toJson(),
        if (msg.locationElem != null) 'locationElem': msg.locationElem!.toJson(),
        if (msg.customElem != null) 'customElem': msg.customElem!.toJson(),
        if (msg.quoteElem != null) 'quoteElem': msg.quoteElem!.toJson(),
        if (msg.atTextElem != null) 'atTextElem': msg.atTextElem!.toJson(),
      }).toList();
      
      final key = 'pinned_messages_${conversationInfo.conversationID}';
      final jsonStringList = pinnedData.map((e) => json.encode(e)).toList();
      await SpUtil().putStringList(key, jsonStringList);
    } catch (e) {
      Logger.print('保存置顶消息失败: $e');
    }
  }

  // 从本地存储加载置顶消息
  Future<void> _loadPinnedMessages() async {
    try {
      final key = 'pinned_messages_${conversationInfo.conversationID}';
      final pinnedData = SpUtil().getStringList(key) ?? [];
      
      pinnedMessages.clear();
      for (final data in pinnedData) {
        try {
          final msgMap = json.decode(data);
          final message = Message.fromJson(msgMap);
          pinnedMessages.add(message);
        } catch (e) {
          Logger.print('解析置顶消息失败: $e');
        }
      }
      
      showPinnedMessages.value = pinnedMessages.isNotEmpty;
    } catch (e) {
      Logger.print('加载置顶消息失败: $e');
    }
  }

  // 移除置顶消息
  void removePinnedMessage(Message message) async {
    pinnedMessages.removeWhere((msg) => msg.clientMsgID == message.clientMsgID);
    await _savePinnedMessages();
    showPinnedMessages.value = pinnedMessages.isNotEmpty;
    
    // 发送取消置顶通知消息给其他群成员
    await _sendPinNotificationMessage(message, false);
    
    IMViews.showToast('已取消置顶');
  }

  // 检查消息是否已置顶
  bool isMessagePinned(Message message) {
    return pinnedMessages.any((msg) => msg.clientMsgID == message.clientMsgID);
  }

  // 切换置顶消息显示状态
  void togglePinnedMessagesVisibility() {
    if (pinnedMessages.isNotEmpty) {
      showPinnedMessages.value = !showPinnedMessages.value;
    }
  }
  // 处理置顶消息点击
  void handlePinnedMessageTap(Message pinnedMessage) {
    // 检查消息是否还存在于当前消息列表中
    final messageExists = messageList.any((msg) => 
      msg.clientMsgID == pinnedMessage.clientMsgID || 
      msg.serverMsgID == pinnedMessage.serverMsgID
    );
    
    if (!messageExists) {
      // 消息已被删除，显示提示对话框
      _showMessageDeletedDialog();
      return;
    }
    
    // 只有自己置顶的消息才能跳转
    if (pinnedMessage.sendID == OpenIM.iMManager.userID) {
      scrollToMessage(pinnedMessage);
    } else {
      IMViews.showToast('无法定位到他人置顶的消息');
    }
  }
  
  // 显示消息已删除的对话框
  void _showMessageDeletedDialog() {
    Get.dialog(
      AlertDialog(
        title: Text('提示'),
        content: Text('该消息已经被删除'),
        actions: [
          TextButton(
            onPressed: () => Get.back(),
            child: Text('确定'),
          ),
        ],
      ),
    );
  }

    // 滚动到指定消息
void scrollToMessage(Message targetMessage) {
    // ... existing code ...
    final index = messageList.indexWhere((msg) => msg.clientMsgID == targetMessage.clientMsgID);
    if (index != -1 && scrollController.hasClients) {
      // 列表通常是新消息在底部，offset=0 在底部，向上滚动 offset 增大
      final reversedIndex = messageList.length - 1 - index;
      final totalExtent = scrollController.position.maxScrollExtent;

      // 采用比例定位，适配不同高度的消息项
      final ratio = messageList.length <= 1 ? 0.0 : reversedIndex / (messageList.length - 1);
      double targetOffset = ratio * totalExtent;

      // 边界保护
      targetOffset = targetOffset.clamp(0.0, totalExtent);

      scrollController.animateTo(
        targetOffset,
        duration: const Duration(milliseconds: 500),
        curve: Curves.easeInOut,
      );

      // IMViews.showToast('已定位到置顶消息');
    } else {
      // IMViews.showToast('消息不在当前聊天记录中');
    }
  }
    // 是否已经全选（仅在多选模式下生效）
  bool get areAllMessagesSelected {
    if (!isMultiSelectMode.value) return false;
    int selectableCount = 0;
    for (final message in messageList) {
      if (_canSelectMessage(message)) {
        selectableCount++;
      }
    }
    return selectableCount > 0 && selectedMessages.length == selectableCount;
  }



}
